﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Automation;
using System.Diagnostics;
using Compiled_Tests.RuleBase;
using Compiled_Tests;
using Compiled_Tests.Utils;
using System.Threading;
using Compiled_Tests.ProcessWrappers;
using LEET.CommonInterfaces.TestControls;
using LEET.Utilities;
using LEET.PlayBack.Controllers;

namespace Compiled_Tests
{
    public class BaseController
    {
        #region Attributes

        protected Box box;
        protected ActionEnumeration Actions { get; private set;}
        protected ITestScript initial;
        public ErrorReportingController ErrorHandling;
        public ProcessController ProcControl;
        public bool IsFinished { get; private set; }
        Queue<IProcess> toWatch;
        ProcessSpawnWatcher spawnWatcher;
        public ImageCapture ic;

        private int lineNumber;
        private List<int> processId;

        #endregion

        #region Event Handlers

        protected delegate void Nil();
        protected delegate void HandleTest(ITestScript t);
        protected delegate void HandleError(Exception e);
        protected delegate void HandleRuleBase(IRuleBase rb);
        protected delegate void HandleProcess(IProcess p);
        protected delegate void HandleSignal(ScriptSignal s);

        protected event HandleTest TestStarted;
        protected event Nil TestFinished;
        protected event Nil ActionTaken;
        protected event HandleSignal SignalRecieved;
        protected event HandleError ErrorRecieved;
        protected event HandleRuleBase RuleBaseRecieved;
        protected event HandleProcess ProcessRecieved;
        protected event Nil Termination;

        #endregion

        public BaseController(ITestScript initial)
        {
            box = new Box();
            Actions = new ActionEnumeration();
            Actions.OnScriptFinished += delegate() { TestFinished(); };
            
            TestStarted += AddTestToEnum;
            toWatch = new Queue<IProcess>();

            ProcControl = new ProcessController(toWatch, box);
            ProcessRecieved += ProcControl.Enque;
            ProcessRecieved += ProcControl.Target;

            ErrorHandling = new ErrorReportingController();
            TestStarted += ErrorHandling.OnTestStarted;
            TestFinished += ErrorHandling.OnTestFinished;
            RuleBaseRecieved += ErrorHandling.OnRuleBaseRecieved;
            SignalRecieved += ErrorHandling.OnSignalRecieved;
            ProcessRecieved += ErrorHandling.OnProcessRecieved;
            ActionTaken += ErrorHandling.OnActionTaken;
            ErrorRecieved += ErrorHandling.OnErrorRecieved;
            
            this.initial = initial;
            IsFinished = false;

            TestFinished += delegate() { };
            ActionTaken += delegate() { };
            SignalRecieved += delegate(ScriptSignal sig) { };
            RuleBaseRecieved += delegate(IRuleBase rb) { };

            processId = new List<int>();
            spawnWatcher = new ProcessSpawnWatcher(toWatch);
            spawnWatcher.OnSpawn += ProcessSpawned;
            ProcessRecieved += delegate(IProcess proc) { };

            ic = new ImageCapture(box);
            TestStarted += ic.OnTestStarted;
            TestFinished += ic.OnTestFinished;
            ActionTaken += ic.OnActionTaken;
            SignalRecieved += ic.OnSignalRecieved;
            ErrorRecieved += ic.OnErrorRecieved;
            RuleBaseRecieved += ic.OnRuleBaseRecieved;
            ProcessRecieved += ic.OnProcessRecieved;
            Termination += ic.OnTermination;

            Thread t = new Thread(new ThreadStart(spawnWatcher.Run));
            t.Start();
        }

        public virtual void Run()
        {
            try
            {
                FireEvent(new AutomationActionResult(initial));
                while (Actions.MoveNext())
                {
                    IAutomationActionResult result = Actions.Current;
                    FireEvent(result);
                }
            }
            finally
            {
                ShutDown();
            }
        }

        private void ProcessSpawned(Process p)
        {
            IProcess proc = new ProcessWrapper(p, box.MakeUnique(p.ProcessName));
            processId.Add(p.Id);
            ProcessRecieved(proc);
        }

        protected void FireEvent(IAutomationActionResult result){
            try{
                if (result.isEmpty)
                {
                    ActionTaken();
                }
                else if (result.isError)
                {
                    throw result.Error;
                }
                else if (result.isProcess)
                {
                    ProcessRecieved(result.Process);
                }
                else if (result.isSignal)
                {
                    SignalRecieved(result.Signal);
                }
                else if (result.isTestScript)
                {
                    TestStarted(result.TestScript());
                }
                else if (result.isRuleBase)
                {
                    RuleBaseRecieved(result.RuleBase());
                }
            }
            catch (Exception e)
            {
                if (e.Message.Contains("Cannot process request because the process"))
                    e = new Exception(ProcControl.DumpErrors, e);
                ErrorRecieved(e);
            }
            Thread.Sleep(box.Delay);
        }

        
        protected void ShutDown()
        {
            Thread.Sleep(1000);
            spawnWatcher.Dispose();
            Termination();
            IsFinished = true;
        }

        
        private void AddTestToEnum(ITestScript t)
        {
            Actions.AddTestScript(t.Run(box));   
        }

    }

}
